package FIT::Fixture::Specify;

use FIT::Class;
use FIT::Document::Transformer;
use aliased 'FIT::Fixture::Runner';

extends 'FIT::Fixture::Base';

augment do_cell => sub {
	my $cell = shift;
	my $col_index = $cell->column_index;
    my $row_index = $cell->row_index;

    # "before" is in 1st cell in table, which can be horizontal or vertical
    if ($col_index == 0 && $row_index == 1) {
        # shadow run the fixtures, so they don't add to the tally
        Runner->new->run_fixtures_silently($cell->first_table);
        return;
    }

    my $actual_cell = $cell->previous_cell || $cell->cell_above;

    transform
        { $self->equal_both($actual_cell, $cell)? Right: Wrong }
        $cell;
};

sub cell_handlers { qw(Skip) } # no default handlers that die on nested tables

# because of how we compare, we could miss differences if we don't go both ways
sub equal_both {
    my ($actual, $expected) = @_;
    return
        $self->equal_tables($actual, $expected) &&
        $self->equal_tables($expected, $actual, 1);
}

# checks if 2 cells contain the same tables
sub equal_tables {
    my ($actual, $expected, $simple_check) = @_;
    for my $actual_e ($actual->descendants) {
        next if $actual_e->tag eq '#PCDATA';
        next if
            $actual_e->parent('td') &&
            $actual_e->parent('td')->att("class") &&
            ($actual_e->parent('td')->att("class") =~ /fit_error/);
        my $xpath = $self->abs2rel($actual, $actual_e);
        my ($expected_e) = $expected->findnodes($xpath);
        return 0 unless $expected_e;
        next if $simple_check;
        return 0 unless $self->equal_elements($actual_e, $expected_e);
    }
    return 1;
}

sub equal_elements {
    my ($actual, $expected) = @_;
    for (keys % { $actual->atts }) {
        next if $_ eq 'fixture_source'; # no need to add this att to specs

        my $actual_attr   = $actual->atts->{$_};
        my $expected_attr = $expected->atts->{$_};
        next if !defined($actual_attr) && !defined($expected_attr);
        return 0 if !defined($actual_attr) || !defined($expected_attr);
        return 0 unless $actual_attr eq $expected_attr;
    }
    return 1 if $actual->tag ne 'td';
    return 1 if $actual->descendants('table');

    # handle errors in cells
    return 1 if
        $actual->att("class") &&
        ($actual->att("class") =~ /fit_error/) &&
        ($expected->trimmed_text eq 'ERROR');

    return $actual->fit_text eq $expected->fit_text;
#print "Actual: [${\( $actual->trimmed_text )}] ne Expected: [${\( $expected->trimmed_text )}]\n";
}

sub abs2rel {
    my ($parent, $child) = @_;
    return substr($child->xpath, length($parent->xpath) + 1);
}

